//Author:       Miguel Dominguez & Heath Cazeau
//Date:         3/9/2011
//Class:        Software Engineering
//Project:      Jaz Interpreter
//File:         InstructionSet.cpp

#include <iostream>
#include <string>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <map>
#include "InstructionSet.h"
#include "Instruction.h"

using namespace std;

namespace operations
{
    //**********************
    //ARITHMETIC
    //**********************
    void Arithmetic::add(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int c = a + b;
        string str = Support::intToStr(c);

        input.push(str);
    }

    void Arithmetic::subtract(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int c = b - a;
        string str = Support::intToStr(c);

        input.push(str);
    }

    void Arithmetic::multiply(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int c = a * b;
        string str = Support::intToStr(c);

        input.push(str);
    }

    void Arithmetic::divide(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int c = b / a;
        string str = Support::intToStr(c);

        input.push(str);
    }
    
    void Arithmetic::modulus(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int c = b % a;
        string str = Support::intToStr(c);

        input.push(str);
    }

    //**********************
    //COMPARISON
    //**********************
    void Comparison::lessThan(stack<string> &input)
    {
        int result = 0;
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int compare_result = Support::compare(b,a);

        if (compare_result==-1)
        {
            result = 1;
        }

        string str = Support::intToStr(result);
        input.push(str);
    }

    void Comparison::lessThanOrEqual(stack<string> &input)
    {
        int result = 0;
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int compare_result = Support::compare(b,a);

        if (compare_result==-1 || compare_result==0)
        {
            result = 1;
        }

        string str = Support::intToStr(result);
        input.push(str);
    }

    void Comparison::greaterThan(stack<string> &input)
    {
        int result = 0;
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int compare_result = Support::compare(b,a);

        if (compare_result==1)
        {
            result = 1;
        }

        string str = Support::intToStr(result);
        input.push(str);
    }

    void Comparison::greaterThanOrEqual(stack<string> &input)
    {
        int result = 0;
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int compare_result = Support::compare(b,a);

        if (compare_result==1 || compare_result==0)
        {
            result = 1;
        }

        string str = Support::intToStr(result);
        input.push(str);
    }

    void Comparison::equal(stack<string> &input)
    {
        int result = 0;
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int compare_result = Support::compare(b,a);

        if (compare_result==0)
        {
            result = 1;
        }

        string str = Support::intToStr(result);
        input.push(str);
    }

    void Comparison::notEqual(stack<string> &input)
    {
        int result = 0;
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int compare_result = Support::compare(b,a);

        if (compare_result != 0)
        {
            result = 1;
        }

        string str = Support::intToStr(result);
        input.push(str);
    }

    //**********************
    //OUTPUT
    //**********************
    void Output::show(string input)
    {
        cout << input << "\n";
    }

    void Output::print(stack<string> &input)
    {
        string str = input.top();
        cout << str << "\n";
    }

    //**********************
    //BRANCH
    //**********************
    Instruction * Branch::goTo(map<string, Instruction*> labelLocs, string argument)
    {
        return labelLocs[argument];
    }

    Instruction * Branch::halt()
    {
        return NULL;
    }

    Instruction * Branch::goTrue(stack<string> &input, map<string, Instruction *> labelLocs, string argument, Instruction * current)
    {
        string str = input.top();
        Instruction * output;
        input.pop();
        if(str.compare("0") != 0)
        {
            output = labelLocs[argument];
        }
        else
        {
            output = (*current).getNextInstruction();
        }
        return output;
    }

    Instruction * Branch::goFalse(stack<string> &input, map<string, Instruction *> labelLocs, string argument, Instruction * current)
    {
        string str = input.top();
        Instruction * output;
        input.pop();
        if(str.compare("0") == 0)
        {
            output = labelLocs[argument];
        }
        else
        {
            output = (*current).getNextInstruction();
        }
        return output;
    }

    //**********************
    //LOGICAL
    //**********************
    void Logical::logicalAnd(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int c = a & b;
        string str = Support::intToStr(c);
        input.push(str);
    }

    void Logical::logicalOr(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int b = Support::strToInt(input.top());
        input.pop();

        int c = a | b;
        string str = Support::intToStr(c);
        input.push(str);
    }

    void Logical::logicalNot(stack<string> &input)
    {
        int a = Support::strToInt(input.top());
        input.pop();

        int c = ~a;
        string str = Support::intToStr(c);
        input.push(str);
    }

    //**********************
    //STACK MANIPULATION
    //**********************
    //data.insert(pair<string,int>("string",12345));
    //int value = data["string"];
    void StackManipulation::push(stack<string> &input, string str)
    {
        input.push(str);
    }

    void StackManipulation::pop(stack<string> &input)
    {
        input.pop();
    }

    void StackManipulation::rvalue(stack<string> &input, map<string, int> &RMem, string var)
    {
        int value = RMem[var];
        string str = Support::intToStr(value);
        input.push(str);
    }

    void StackManipulation::lvalue(stack<string> &input, string var)
    {
        input.push(var);
    }

    void StackManipulation::assign(stack<string> &input, map<string, int> &LMem)
    {
        string rval = input.top();
        input.pop();

        string lval = input.top();
        input.pop();

        int rval_int = Support::strToInt(rval);
        LMem[lval] = rval_int;
    }

    void StackManipulation::copy(stack<string> &input)
    {
        input.push(input.top());
    }
}

//**********************
//SUPPORTING METHODS
//**********************
int Support::strToInt(string &str)
{
    const char* ch = str.c_str ();
    int a = atoi(ch);
    return a;
}

string Support::intToStr(int num)
{
    char buffer [100];
    snprintf(buffer, 100, "%d", num);
    string output(buffer);
    return output;
}

//Returns:
//-1: a < b
// 0: a = b
// 1: a > b
int Support::compare(int b, int a)
{
    int result;
    if(b > a)
    {
        result=1;
    }

    else if(b < a)
    {
        result=-1;
    }

    else
    {
        result=0;
    }

    return result;
}